package xyz.zhno.project.gateway.handler;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import xyz.zhno.project.commons.core.dto.ResultError;
import xyz.zhno.project.commons.core.exception.ProjectException;
import xyz.zhno.project.commons.core.exception.ProjectExceptionCodeEnum;
import xyz.zhno.project.commons.core.exception.ProjectJsonException;

/**
 * 网关全局异常处理
 * @author zh_o
 * @date 2020-11-01
 */
@Slf4j
@Order(-1)
@Component
public class GlobalErrorHandler implements ErrorWebExceptionHandler {

    private final ObjectMapper JSON = new ObjectMapper();

    @Override
    @SuppressWarnings("all")
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        ServerHttpResponse response = exchange.getResponse();
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();
        ResultError re = null;
        // 不拦截图标
        String icon = "/favicon.ico";
        if (icon.equals(request.getPath())) {
            return response.setComplete();
        }
        // 响应状态异常
        if (ex instanceof ResponseStatusException) {
            log.error("Exception: {}, Message: {}, Time: {} " + "\r\n" + " StackTrace: [{}]", ex.getClass(), ex.getMessage(), DateUtil.now(), ExceptionUtil.stacktraceToString(ex));
            re = ResultError.builder()
                    .code(ProjectExceptionCodeEnum.SYS_RESOURCE_NOT_FOUND.getCode())
                    .message(ProjectExceptionCodeEnum.SYS_RESOURCE_NOT_FOUND.getMessage())
                    .build();
        }
        // 自定义异常
        if (ex instanceof ProjectException) {
            ProjectException we = (ProjectException) ex;
            log.error("Exception: {}, Message: {}, Time: {} " + "\r\n" + " StackTrace: [{}]", ex.getClass(), ex.getMessage(), DateUtil.now(), ExceptionUtil.stacktraceToString(ex));
            re = ResultError.error(we);
        }
        // 其他异常
        log.error("Exception: {}, Message: {}, Time: {} " + "\r\n" + " StackTrace: [{}]", ex.getClass(), ex.getMessage(), DateUtil.now(), ExceptionUtil.stacktraceToString(ex));
        re = ResultError
                .builder()
                .code(ProjectExceptionCodeEnum.SYS_ERROR.getCode())
                .message(ProjectExceptionCodeEnum.SYS_ERROR.getMessage())
                .build();
        // 设置响应类型
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        // 修改响应状态码
        response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        // 将响应结果集转换为字节数组
        byte[] b = null;
        try {
            b = JSON.writeValueAsBytes(re);
        } catch (JsonProcessingException e) {
            log.error("Exception: {}, Message: {}, Time: {} " + "\r\n" + " StackTrace: [{}]", e.getClass(), e.getMessage(), DateUtil.now(), ExceptionUtil.stacktraceToString(e));
            throw new ProjectJsonException(ProjectExceptionCodeEnum.TOKEN_INVALID);
        }
        return response.writeWith(Mono.just(response.bufferFactory().wrap(b)));
    }
}
